/*
 * Copyright (C) 2006 Johan Maasing johan at zoom.nu Licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
 * or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package nu.zoom.catonine.desktop.rule;

import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;

import nu.zoom.catonine.fontchooser.FontChooserFactory;
import nu.zoom.catonine.prefs.Preferences;
import nu.zoom.catonine.stylerule.SampleStyleRulesFactory;
import nu.zoom.catonine.stylerule.StyleRules;
import nu.zoom.catonine.stylerule.StyleRulesManager;
import nu.zoom.catonine.xml.StyleRulesManagerPersistence;
import nu.zoom.catonine.xml.StyleRulesManagerPersistence.FileCanNotBeReadException;
import nu.zoom.gal.error.ErrorReporter;
import nu.zoom.swing.desktop.Workbench;
import nu.zoom.swing.desktop.WorkbenchFrame;
import nu.zoom.swing.desktop.WorkbenchListener;
import nu.zoom.swing.desktop.WorkbenchMenuBar;
import nu.zoom.swing.desktop.common.BackendException;
import nu.zoom.swing.desktop.common.action.ToggleFrameAction;
import nu.zoom.swing.desktop.component.filechooser.FileChooser;
import nu.zoom.swing.desktop.preferences.InvalidDataTypeException;
import nu.zoom.ui.Resources;
import nu.zoom.ui.Resources.ResourceNotFoundException;

/**
 * A plug-in for the zoom.nu desktop to manage collections of style rules.
 * 
 * @author "Johan Maasing" &lt;johan@zoom.nu&gt;
 * 
 */
public class StyleRulesPlugInImpl implements StyleRulesPlugIn,
		WorkbenchListener {
	private final Logger log = Logger.getLogger(getClass().getName());
	private StyleRulesManager manager;
	private final Workbench workbench;
	private final Resources messages;
	private final Preferences preferences;
	private final ErrorReporter errorReporter;
	private final FontChooserFactory fontChooserFactory;
	final ArrayList<StyleRulesPlugInListener> listeners = new ArrayList<StyleRulesPlugInListener>();
	private WorkbenchFrame managerFrame = null;

	public StyleRulesPlugInImpl(final Workbench workbench,
			final Resources messages, final Preferences preferences,
			final ErrorReporter errorReporter,
			final FontChooserFactory fontChooserFactory) {
		super();
		this.workbench = workbench;
		this.messages = messages;
		this.preferences = preferences;
		this.errorReporter = errorReporter;
		this.fontChooserFactory = fontChooserFactory;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see nu.zoom.swing.desktop.PlugIn#initialize()
	 */
	@Override
	public void initialize() throws Exception {
		workbench.addWorkBenchListener(this);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see nu.zoom.swing.desktop.WorkbenchListener#start()
	 */
	@Override
	public void start() {
		final ShowStyleRulesAction showStyleRulesAction;
		try {
			showStyleRulesAction = new ShowStyleRulesAction(
					messages.getMessage("nu.zoom.catonine.configurations"));
			final WorkbenchMenuBar menuBar = workbench.getMenuBar();
			menuBar.addToApplicationMenu(showStyleRulesAction.getControl());
			menuBar.addToApplicationMenu(new JMenuItem(
					new SaveStyleRulesManagerAsAction(this, messages)));
			menuBar.addToApplicationMenu(new JMenuItem(
					new ImportStyleRulesManagerAction(this, messages,
							errorReporter)));
			workbench.registerKeyboardAction(showStyleRulesAction, KeyStroke
					.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK),
					JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
			try {
				if (!loadManager()) {
					manager = new StyleRulesManager();
					manager.add(SampleStyleRulesFactory.getLog4JStyleRules());
					manager.add(SampleStyleRulesFactory.getSyslogStyleRules());
					manager.resetChangeStatus();
				}
			} catch (InvalidDataTypeException invalidDataTypeException) {
				errorReporter.reportError(invalidDataTypeException);
			} catch (BackendException backendException) {
				errorReporter.reportError(backendException);
			} catch (IOException e) {
				errorReporter.reportError(e);
			}
		} catch (Resources.ResourceNotFoundException resourceNotFoundException) {
			errorReporter.reportError(resourceNotFoundException);
		}
	}

	@Override
	public void close() {
		if (manager.isChanged()) {
			saveManager(false);
		}
	}

	@Override
	public void visible() {
	}

	public class ShowStyleRulesAction extends
			ToggleFrameAction<JCheckBoxMenuItem> {

		private static final long serialVersionUID = 1L;

		public ShowStyleRulesAction(final String name) {
			super(new JCheckBoxMenuItem());
			setName(name);
			setMnemonicKey(KeyEvent.VK_N);
			setAcceleratorKey(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK);
		}

		@Override
		protected WorkbenchFrame createFrame() {
			try {
				String title = getFrameTitle();
				StyleRulesPlugInImpl.this.managerFrame = workbench
						.createWorkbenchFrame("StyleRulesColoring",
								getStyleRulesManagerComponent(), null, true,
								true);
				StyleRulesPlugInImpl.this.managerFrame.setTitle(title);
				return StyleRulesPlugInImpl.this.managerFrame;
			} catch (ResourceNotFoundException ex) {
				errorReporter.reportError(ex);
				return null;
			}
		}

		@Override
		public void frameClosed(WorkbenchFrame frame) {
			super.frameClosed(frame);
			StyleRulesPlugInImpl.this.managerFrame = null;
		}

		@Override
		public void frameDetached(WorkbenchFrame frame) {
		}

		@Override
		public void frameAttached(WorkbenchFrame frame) {
		}
	}

	/**
	 * @return
	 * @throws ResourceNotFoundException
	 */
	protected String getFrameTitle() throws ResourceNotFoundException {
		final String title = messages
				.getMessage("nu.zoom.catonine.configurations");
		return title;
	}

	/**
	 * Instance of GUI component. Should not be adressed directly, use the lazy
	 * constructor method.
	 */
	StyleRulesManagerComponent styleRulesManagerComponent;

	/**
	 * Lazy constructor of component
	 * 
	 * @throws ResourceNotFoundException
	 */
	private StyleRulesManagerComponent getStyleRulesManagerComponent()
			throws ResourceNotFoundException {
		if (this.styleRulesManagerComponent == null) {
			final StyleRulesManagerComponent newStyleRulesManagerComponent = new StyleRulesManagerComponent(
					manager, messages, workbench, fontChooserFactory,
					errorReporter);
			this.styleRulesManagerComponent = newStyleRulesManagerComponent;
		}
		return this.styleRulesManagerComponent;
	}

	private boolean loadManager() throws InvalidDataTypeException,
			BackendException, ResourceNotFoundException, IOException {
		boolean success = loadManager(FileUtils.getConfigurationFilename());
		return success;
	}

	public void importManager() throws ResourceNotFoundException, IOException,
			InvalidDataTypeException, BackendException {
		final FileChooser fileChooser = preferences
				.getConfiguratonFilechooser();
		File openFile = fileChooser.openFile();
		if (openFile != null) {
			StyleRulesManager importFrom = readManager(openFile);
			if (importFrom != null) {
				// workbench.startWorkIndicator();
				this.manager.importManager(importFrom);
				workbench.getStatusbar().setMessage(
						messages.format("nu.zoom.catonine.import.from",
								openFile.getPath()));
				// workbench.stopWorkIndicator();
			}
		}
	}

	private boolean loadManager(final File managerFileName)
			throws ResourceNotFoundException, IOException {
		if (managerFileName == null) {
			return false;
		}
		if ((manager != null) && (manager.isChanged())) {
			saveManager(false);
		}
		StyleRulesManager newManager = readManager(managerFileName);
		// If we load a new manager when an old is opened we should inform
		// listeners (GUI elements) that the rules have changed.
		if (newManager != null) {
			if (manager != null) {
				manager.copyListenersTo(newManager);
			}
			manager = newManager;
			return true;
		}
		return false;
	}

	private StyleRulesManager readManager(File managerFile)
			throws ResourceNotFoundException, IOException {
		StyleRulesManager newManager = null;
		try {
			newManager = StyleRulesManagerPersistence.readManager(managerFile);
		} catch (FileNotFoundException exc) {
			if (log.isLoggable(Level.INFO)) {
				final String message = messages.format(
						"nu.zoom.catonine.open.fail.noexist",
						managerFile.getCanonicalPath());
				log.info(message);
			}
		} catch (FileCanNotBeReadException exc) {
			final String message = messages.format(
					"nu.zoom.catonine.open.fail.noread",
					managerFile.getCanonicalPath());
			errorReporter.reportError(message);

		} catch (Exception e) {
			errorReporter.reportError(messages
					.getMessage("nu.zoom.catonine.configurations.load.failed"),
					e);
			newManager = null;
		}
		return newManager;
	}

	public void saveManager(final boolean saveAs) {
		try {
			final File saveFile;
			if (saveAs) {
				FileChooser fileChooser = preferences
						.getConfiguratonFilechooser();
				saveFile = fileChooser.saveFile();
				if (saveFile == null) {
					// Cancelled
					return;
				}
			} else {
				saveFile = FileUtils.getConfigurationFilename();
			}
			StyleRulesManagerPersistence.save(manager, saveFile);
			manager.resetChangeStatus();
		} catch (Exception e) {
			try {
				errorReporter
						.reportError(
								messages.getMessage("nu.zoom.catonine.configurations.save.failed"),
								e);
			} catch (ResourceNotFoundException ex) {
				errorReporter.reportError(ex);
			}
		}
	}

	@Override
	public StyleRulesManager getStyleRulesManager() {
		return manager;
	}

	@Override
	public void editStyleRules(StyleRules styleRules)
			throws ResourceNotFoundException {
		if (styleRules != null) {
			StyleRulesManagerComponent styleRulesManagerComponent = getStyleRulesManagerComponent();
			styleRulesManagerComponent.editStyleRules(styleRules);
		}
	}

	@Override
	public synchronized void addListener(StyleRulesPlugInListener listener) {
		if (listener != null) {
			listeners.add(listener);
		}
	}

	@Override
	public synchronized void removeListener(StyleRulesPlugInListener listener) {
		listeners.remove(listener);
	}

	@Override
	public void createSimpleRule(StyleRules rules, String selectedText)
			throws ResourceNotFoundException {
		getStyleRulesManagerComponent().createSimpleRule(rules, selectedText);
	}
}
